/**
    Copyright (c) 2010 yakiimo02
    Distributed under the New BSD License.
    See included license.txt or http://www.yakiimo3d.com/NewBSDLicense.txt
**/

#pragma once

#include "Nullstein/TaskScheduler.h"
#include "Nullstein/ParallelFor.h"

#include "optimized/SmallPtGeometryOPT.h"
#include "optimized/SmallPtStructs.h"
#include "optimized/SphereSOA.h"
#include "optimized/SmallPtDefines.h"
 
#include <vector>

namespace OPT
{

class SmallPtCPU_MT;

/**
    @brief   Nullstein For task.
    @author  yakiimo02
    @date    2010/08
*/
class TracePixelTask : public IForTask
{
public:
    inline TracePixelTask() : m_pSmallPT_MT( NULL ) {};

    virtual bool DoRange(CWorkerThread* pThread, const CRange &Range) const;

    inline bool Intersect( const Ray &r, SPT_FLOAT_OPT &t, int &id ) const;

    inline void SetpSmallPtCPU_MT( SmallPtCPU_MT* pSmallPT_MT ) { m_pSmallPT_MT = pSmallPT_MT; }

protected:
    Vec TracePixelTask::Radiance( const Ray &r_ ) const;

protected:
    SmallPtCPU_MT*  m_pSmallPT_MT;
};

/**
    Ray-sphere intersection.
    @param r  [in]  Ray.
    @param t  [out] Ray-sphere shortest intersection distance.
    @param id [out] Ray-sphere shortest inersection sphere index id.
    @return Whether a ray-sphere intersection occurred.
*/
inline bool TracePixelTask::Intersect( const Ray &r, SPT_FLOAT_OPT &t, int &id ) const
{
// SOA SIMD
#if SPT_SOA_RAY_SPHERE_INTERSECT
    RaySOA raySOA;
    raySOA.Init( r );

    __m128 distance;
    const SPT_FLOAT_OPT inf = 1e20f;
    t = 1e20f;
        
    __declspec( align( 16 ) ) float distanceA[ 12 ];

    distance = IntersectSOA( raySOA, 0 );
    _mm_store_ps( &distanceA[ 0 ], distance );

    distance = IntersectSOA( raySOA, 1 );
    _mm_store_ps( &distanceA[ 4 ], distance );
    
    distance = IntersectSOA( raySOA, 2 );
    _mm_store_ps( &distanceA[ 8 ], distance );

    for( int i=0; i<SPHERE_COUNT; ++i ) 
    {
        // Find the ray's closest intersecting sphere.
        if( distanceA[ i ] !=0.0f && distanceA[ i ]<t )
        {
            t = distanceA[ i ];
            id = i;
        }
    }

    return t<inf;
// non SIMD
#else
    SPT_FLOAT_OPT distance;
    const SPT_FLOAT_OPT inf=t=1e20f;

    for( int i=SPHERE_COUNT; i--; ) 
    {
        // Find the ray's closest intersecting sphere.
        distance = spheresOPT[ i ].intersect( r, ( id != i ) );
        if( distance!=0.0f && distance<t )
        {
            t=distance;
            id=i;
        }
    }

    return t<inf;
#endif
}

/**
    @brief   Nullstein root task.
    @author  yakiimo02
    @date    2010/08
*/
class RootTask : public CInternalTask
{
public:
	inline RootTask( CTaskCompletion* pCompletion ) : CInternalTask( pCompletion ), m_nPixelCnt ( 0 ), m_pSmallPT_MT( NULL )
	{
	}
	
	bool Run( CWorkerThread* pThread );

    inline void SetpSmallPtCPU_MT( SmallPtCPU_MT* pSmallPT_MT ) { m_pSmallPT_MT = pSmallPT_MT; }
    inline void SetPixelCnt( int nPixelCnt ) { m_nPixelCnt = nPixelCnt; }

protected:
    int                 m_nPixelCnt;
    SmallPtCPU_MT*      m_pSmallPT_MT;
};

}